/*-----------------------------------------------------------------------------
 * @file hal_i2c.c
 * @brief Implement interface of I2C Module
 * @author WLH<wanglehui8357@163.com>
-----------------------------------------------------------------------------*/
#include "hal_i2c.h"
#include "hal_hrt.h"
#include "board.h"

/*-----------------------------------------------------------------------------
 * marcro define
-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * Macros define
-----------------------------------------------------------------------------*/
#define I2C_TIMEOUT_MS 30U
#define I2C_BUS_CNT    3U

/*-----------------------------------------------------------------------------
 * local variable
-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * local funtions
-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * Public funtions
-----------------------------------------------------------------------------*/
bool hal_i2c_init(const U8 port_num, i2c_clock_speed clk_hz)
{
    UNUSED(clk_hz);

    if (port_num == HAL_I2C0)
    {
        // Configure the peripheral clock
        rcu_periph_clock_enable(RCU_I2C0);
        rcu_periph_clock_enable(RCU_GPIOB);

        // Configure the I2C GPIO ports
        gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_6);
        gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_7);
        gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
        gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
        gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
        gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

        // Configure the I2C interface
        i2c_timing_config(I2C0, 0, 0x3, 0);
        i2c_master_clock_config(I2C0, 0x13, 0x36);
        i2c_enable(I2C0);
        return true;
    }

    if (port_num == HAL_I2C1)
    {
        // Configure the peripheral clock
        rcu_periph_clock_enable(RCU_I2C2);
        rcu_periph_clock_enable(RCU_GPIOA);

        // Configure the I2C GPIO ports
        gpio_af_set(GPIOA, GPIO_AF_4, GPIO_PIN_6);
        gpio_af_set(GPIOA, GPIO_AF_4, GPIO_PIN_7);
        gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
        gpio_output_options_set(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
        gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
        gpio_output_options_set(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

        // configure the I2C interface
        i2c_timing_config(I2C2, 0, 0x3, 0);
        i2c_master_clock_config(I2C2, 0x13, 0x36);
        i2c_enable(I2C2);

        return true;
    }

    return false;
}

bool hal_i2c_read_data(const U8 port_num, U8 addr, U8 reg, U8* data, U16 count)
{
    uint32_t i2cx;
    if (port_num == HAL_I2C0)
    {
        i2cx = I2C0;
    }
    else if (port_num == HAL_I2C1)
    {
        i2cx = I2C2;
    }
    else
    {
        return false;
    }

    U32 start_ts = hal_hrt_get_ms();

    /* clear I2C_TDATA register */
    I2C_STAT(i2cx) |= I2C_STAT_TBE;
    /* send slave address to I2C bus */
    i2c_master_addressing(i2cx, addr, I2C_MASTER_TRANSMIT);
    /* configure number of bytes to be transferred */
    i2c_transfer_byte_number_config(i2cx, 1);
    /* disable I2C automatic end mode in master mode */
    i2c_automatic_end_disable(i2cx);
    /* wait until I2C bus is idle */
    while (i2c_flag_get(i2cx, I2C_FLAG_I2CBSY))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }
    /* send a start condition to I2C bus */
    i2c_start_on_bus(i2cx);
    /* wait until the transmit data buffer is empty */
    while (SET != i2c_flag_get(i2cx, I2C_FLAG_TBE))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }
    /* send the internal address */
    i2c_data_transmit(i2cx, reg);
    /* wait until TC bit is set */
    while (!i2c_flag_get(i2cx, I2C_FLAG_TC))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }
    /* send slave address to I2C bus */
    i2c_master_addressing(i2cx, addr|0x01, I2C_MASTER_RECEIVE);

    /* configure number of bytes to be transferred */
    i2c_transfer_byte_number_config(i2cx, count);
    /* enable I2C automatic end mode in master mode */
    i2c_automatic_end_enable(i2cx);

    /* send a start condition to I2C bus */
    i2c_start_on_bus(i2cx);
    while (count > 0)
    {
        /* wait until the RBNE bit is set and clear it */
        if (i2c_flag_get(i2cx, I2C_FLAG_RBNE))
        {
            /* read a byte from the EEPROM */
            *data = i2c_data_receive(i2cx);
            /* point to the next location where the byte read will be saved */
            data++;
            /* decrement the read bytes counter */
            count--;
        }
    }

    /* wait until the stop condition is finished */
    while (!i2c_flag_get(i2cx, I2C_FLAG_STPDET))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }
    /* clear STPDET flag */
    i2c_flag_clear(i2cx, I2C_FLAG_STPDET);
    return true;
}

bool hal_i2c_write_data(const U8 port_num, U8 addr, U8 reg, U8* data, U16 count)
{
    uint32_t i2cx;
    if (port_num == HAL_I2C0)
    {
        i2cx = I2C0;
    }
    else if (port_num == HAL_I2C1)
    {
        i2cx = I2C2;
    }
    else
    {
        return false;
    }

    U32 start_ts = hal_hrt_get_ms();
    /* send slave address to I2C bus */
    i2c_master_addressing(i2cx, addr, I2C_MASTER_TRANSMIT);
    /* configure number of bytes to be transferred */
    i2c_transfer_byte_number_config(i2cx, count + 1);
    /* enable I2C automatic end mode in master mode */
    i2c_automatic_end_enable(i2cx);
    /* clear I2C_TDATA register */
    I2C_STAT(i2cx) |= I2C_STAT_TBE;
    /* wait until I2C bus is idle */
    while (i2c_flag_get(i2cx, I2C_FLAG_I2CBSY))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }

    /* send a start condition to I2C bus */
    i2c_start_on_bus(i2cx);

    /* wait until the transmit data buffer is empty */
    while (SET != i2c_flag_get(i2cx, I2C_FLAG_TBE))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }

    /* send the internal address: only one byte address */
    i2c_data_transmit(i2cx, reg);

    /* while there is data to be written */
    while (count > 0u)
    {
        /* wait until TI bit is set */
        while (!i2c_flag_get(i2cx, I2C_FLAG_TI))
        {
            if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
        }
        i2c_data_transmit(i2cx, *data);
        data ++;
        count --;
    }
    /* wait until the stop condition is finished */
    while (!i2c_flag_get(i2cx, I2C_FLAG_STPDET))
    {
        if(hal_hrt_elapsed_ms(start_ts) > I2C_TIMEOUT_MS) return false;
    }
    /* clear STPDET flag */
    i2c_flag_clear(i2cx, I2C_FLAG_STPDET);

    return true;
}

/*-----------------------------------------------------------------------------
 * ISR function override
-----------------------------------------------------------------------------*/
